package com.gitee.qdbp.general.common.biz.sequence.service;

import static org.springframework.transaction.annotation.Isolation.READ_COMMITTED;
import static org.springframework.transaction.annotation.Propagation.REQUIRES_NEW;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.alibaba.fastjson.JSON;
import com.gitee.qdbp.able.exception.ServiceException;
import com.gitee.qdbp.able.result.ResultCode;
import com.gitee.qdbp.general.common.api.sequence.model.StrongSequenceBean;
import com.gitee.qdbp.general.common.api.sequence.model.StrongSequenceRule;
import com.gitee.qdbp.general.common.api.sequence.model.StrongSequenceUpdate;
import com.gitee.qdbp.general.common.api.sequence.model.StrongSequenceWhere;
import com.gitee.qdbp.general.common.biz.sequence.basic.StrongSequenceBasic;
import com.gitee.qdbp.general.common.biz.sequence.dao.ISequenceCallFunctionDao;
import com.gitee.qdbp.general.common.biz.sequence.service.model.StrongSequenceParams;
import com.gitee.qdbp.general.common.biz.sequence.service.model.StrongSequenceResult;
import com.gitee.qdbp.general.common.error.GeneralErrorCode;
import com.gitee.qdbp.tools.utils.JsonTools;
import com.gitee.qdbp.tools.utils.VerifyTools;

/**
 * 复杂序列号业务处理类
 *
 * @author zhaohuihua
 * @version 171126
 */
@Service
// REQUIRES_NEW = 独立事务: 即使调用方操作失败, 序列号也不应回滚
// READ_COMMITTED = 其他事务提交之后可以再次读取, 如果不设置则再次尝试时返回的仍然是之前的值
@Transactional(propagation = REQUIRES_NEW, isolation = READ_COMMITTED, rollbackFor = Throwable.class)
public class StrongSequenceImpl {

    /** 日志对象 **/
    private static final Logger log = LoggerFactory.getLogger(StrongSequenceImpl.class);

    @Autowired
    private StrongSequenceBasic strongSequenceBasic;

    @Autowired
    private ISequenceCallFunctionDao sequenceCallFunctionDao;

    /** 执行SQL, 生成下一个值, 先查询当前值, 再更新为新值 **/
    public StrongSequenceResult execute(String type, String subtype, StrongSequenceRule rule, StrongSequenceQueryer queryer)
            throws ServiceException {
        String msg = "Failed to generate StrongSequence. ";

        if (VerifyTools.isBlank(type)) {
            log.error(msg + "params is null: type");
            throw new ServiceException(ResultCode.PARAMETER_IS_REQUIRED);
        }

        Long prevValue = null;
        StrongSequenceBean bean;
        { // 查询当前值
            StrongSequenceWhere where = new StrongSequenceWhere();
            where.setType(type);
            where.setSubtype(subtype);
            bean = strongSequenceBasic.find(where);
        }
        if (bean == null) { // 当前值为空, 新增一条记录
            StrongSequenceRule dbRule = queryer.findRule(type);
            StrongSequenceRule realRule = dbRule != null ? dbRule : rule;
            if (realRule.getCurrValue() == null || realRule.getCurrValue() <= 0) {
                SequenceTools.next(realRule);
            }

            StrongSequenceBean model = realRule.to(StrongSequenceBean.class);
            model.setType(type);
            model.setSubtype(subtype);
            model.setId(null);
            model.setCreateTime(null);
            strongSequenceBasic.create(model);

            StrongSequenceResult result = realRule.to(StrongSequenceResult.class);
            result.setPrevValue(prevValue);
            return result;
        } else { // 当前值不为空, 更新为新值
            // 构建更新参数
            StrongSequenceWhere where = new StrongSequenceWhere();
            where.setId(bean.getId());
            where.setType(type);
            where.setSubtype(subtype);
            where.setTimeFormat(bean.getTimeFormat());
            where.setDigitNumber(bean.getDigitNumber());
            where.setInitValue(bean.getInitValue());
            where.setCurrValue(bean.getCurrValue());
            where.setCurrTime(bean.getCurrTime());
            where.setRound(bean.getRound());

            StrongSequenceUpdate update = new StrongSequenceUpdate();
            update.setWhere(where);

            // 检查默认值
            if (VerifyTools.isBlank(bean.getTimeFormat())) {
                bean.setTimeFormat(rule.getTimeFormat());
            }
            if (VerifyTools.isBlank(bean.getDigitNumber()) || bean.getDigitNumber() <= 0) {
                bean.setDigitNumber(rule.getDigitNumber());
            }

            // 取当前值作为更新后的原值
            if (bean.getCurrValue() != null && bean.getCurrValue() > 0) {
                prevValue = bean.getCurrValue();
            }
            // 计算下一个值
            SequenceTools.next(bean);
            update.setCurrTime(bean.getCurrTime());
            update.setCurrValue(bean.getCurrValue());

            // 将当前值更新为新值
            int rows = strongSequenceBasic.update(update, false);
            if (rows == 0) {
                // 受影响行数为0, 说明其他线程已经修改掉这条记录了
                throw new ServiceException(GeneralErrorCode.CONCURRENCY_CONFLICT);
            }

            StrongSequenceResult result = bean.to(StrongSequenceResult.class);
            result.setPrevValue(prevValue);
            return result;
        }
    }

    /** 调用存储过程生成下一个值 **/
    public StrongSequenceResult callFunction(String type, String subtype, StrongSequenceRule rule)
            throws ServiceException {
        String msg = "Failed to generate StrongSequence. ";

        if (VerifyTools.isBlank(type)) {
            log.error(msg + "params is null: type");
            throw new ServiceException(ResultCode.PARAMETER_IS_REQUIRED);
        }

        // 构建参数
        StrongSequenceParams params = rule.to(StrongSequenceParams.class);
        params.setType(type);
        params.setTenantCode(subtype);

        String string;
        try {
            // 调用存储过程
            string = sequenceCallFunctionDao.callStrongSequenceNext(params);
        } catch (DuplicateKeyException e) {
            // 主键冲突, 发生在新增规则时, 其他进程已新增此规则
            log.error(msg + e.getClass().getSimpleName() + ".\n\t" + JsonTools.toJsonString(params), e);
            throw new ServiceException(ResultCode.DB_DUPLICATE_KEY, e);
        } catch (Exception e) {
            ServiceException.throwWhenServiceException(e);
            if (e.getMessage().contains("current value changed")) {
                // 并发冲突, 发生在当前值已被其他进程修改时
                log.error(msg + "current value changed.\n\t" + JsonTools.toJsonString(params));
                throw new ServiceException(GeneralErrorCode.CONCURRENCY_CONFLICT, e);
            } else { // 其他错误
                log.error(msg + e.getClass().getSimpleName() + ".\n\t" + JsonTools.toJsonString(params), e);
                throw new ServiceException(ResultCode.DB_UPDATE_ERROR, e);
            }
        }
        return JSON.parseObject(string, StrongSequenceResult.class);
    }
}
